﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Dynamic;

namespace DataAccess
{
    /// <summary>
    /// 提供多数据库访问的一系列方法，并在线程上安全。(首次使用时自动实例化配置文件中connectionStrings的配置信息)
    /// </summary>
    public class DbAccess : DynamicObject
    {
        private static DbAccess _instance;
        private static readonly object _sync = new object();
        private Hashtable _entries;
        private static string _defaultDb;

        /// <summary>
        /// 返回数据库实体<see cref="DataAccess.Database"/>的集合
        /// </summary>
        public static DbAccess Db
        {
            get
            {
                if (_instance == null)
                {
                    lock (_sync)
                    {
                        if (_instance == null)
                        {
                            _instance = new DbAccess();
                        }
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// 通过应用程序配置文件初始化多数据库实例
        /// </summary>
        private DbAccess()
        {
            _entries = new Hashtable();
            for (int i = 0; i < ConfigurationManager.ConnectionStrings.Count; i++)
            {
                ConnectionStringSettings cs = ConfigurationManager.ConnectionStrings[i];
                Add(cs.Name, cs.ProviderName, cs.ConnectionString);
                if (string.IsNullOrEmpty(_defaultDb))
                {
                    _defaultDb = cs.Name;
                }
            }
        }

        /// <summary>
        /// 根据名称返回数据库实体 <see cref="DataAccess.Database"/>
        /// </summary>
        /// <param name="name">数据库配置名称</param>
        /// <returns></returns>
        public Database this[string name]
        {
            get
            {
                return _entries[name] as Database;
            }
            set
            {
                lock (_entries.SyncRoot)
                {
                    _entries[name] = value;
                }
            }
        }

        /// <summary>
        /// 设置默认数据库
        /// </summary>
        /// <param name="name"></param>
        public static void SetDefault(string name)
        {
            if (Db._entries.ContainsKey(name))
            {
                lock (_sync)
                {
                    _defaultDb = name;
                }
            }
        }

        /// <summary>
        /// 获取默认数据库实例，可以通过 SetDefault 方法设置，默认为第一个加入的数据库配置信息。
        /// </summary>
        public static Database Default
        {
            get
            {
                return Db[_defaultDb];
            }
        }

        /// <summary>
        /// 新增数据库实体 <see cref="DataAccess.Database"/> 提供手动添加配置信息的方法
        /// </summary>
        /// <param name="name">数据库配置名称</param>
        /// <param name="providerName">提供程序字符串 如：<see cref="System.Data.SqlClient"/>、<see cref="System.Data.OleDb"/>、<see cref="System.Data.Odbc"/>、<see cref="System.Data.OracleClient"/></param>
        /// <param name="connectionString">连接字符串，建议通过ConnectionBuilder组织连接字符串</param>
        public void Add(string name, string providerName, string connectionString, bool setDefault = false)
        {
            lock (_entries.SyncRoot)
            {
                _entries.Add(name, new Database(name, providerName, connectionString));
                if (setDefault || string.IsNullOrEmpty(_defaultDb))
                {
                    _defaultDb = name;
                }
            }
        }

        public void Add(Database db, bool setDefault)
        {
            lock (_entries.SyncRoot)
            {
                if (db == null)
                {
                    throw new ArgumentNullException("db");
                }
                _entries.Add(db.Name, db);
                if (setDefault || string.IsNullOrEmpty(_defaultDb))
                {
                    _defaultDb = db.Name;
                }
            }
        }

        /// <summary>
        /// 从多数据库实例中移除数据库实体
        /// </summary>
        /// <param name="name">配置名称</param>
        public void Remove(string name)
        {
            lock (_entries.SyncRoot)
            {
                _entries.Remove(name);
            }
        }

        /// <summary>
        /// 实现DynamicObject的动态获取属性的方法
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
        {
            if (binder==null)
            {
                throw new ArgumentNullException("binder");
            }
            base.TryGetMember(binder, out result);
            result = _entries[binder.Name];
            return _entries.ContainsKey(binder.Name);
        }
    }
}
